/*!
 * jQuery Form Plugin
 * version: 2.49 (18-OCT-2010)
 * @requires jQuery v1.3.2 or later
 *
 * Examples and documentation at: http://malsup.com/jquery/form/
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */
;
(function($) {

	/*
	 * Usage Note: ----------- Do not use both ajaxSubmit and ajaxForm on the
	 * same form. These functions are intended to be exclusive. Use ajaxSubmit
	 * if you want to bind your own submit handler to the form. For example,
	 * 
	 * $(document).ready(function() { $('#myForm').bind('submit', function(e) {
	 * e.preventDefault(); // <-- important $(this).ajaxSubmit({ target:
	 * '#output' }); }); });
	 * 
	 * Use ajaxForm when you want the plugin to manage all the event binding for
	 * you. For example,
	 * 
	 * $(document).ready(function() { $('#myForm').ajaxForm({ target: '#output'
	 * }); });
	 * 
	 * When using ajaxForm, the ajaxSubmit function will be invoked for you at
	 * the appropriate time.
	 */

	/**
	 * ajaxSubmit() provides a mechanism for immediately submitting an HTML form
	 * using AJAX.
	 */
	$.fn.ajaxSubmit = function(options) {
		// fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
		if (!this.length) {
			log('ajaxSubmit: skipping submit process - no element selected');
			return this;
		}

		if (typeof options == 'function') {
			options = {
				success : options
			};
		}

		var url = $.trim(this.attr('action'));
		if (url) {
			// clean url (don't include hash vaue)
			url = (url.match(/^([^#]+)/) || [])[1];
		}
		url = url || window.location.href || '';

		options = $
				.extend(
						true,
						{
							url : url,
							type : this.attr('method') || 'GET',
							iframeSrc : /^https/i
									.test(window.location.href || '') ? 'javascript:false'
									: 'about:blank'
						}, options);

		// hook for manipulating the form data before it is extracted;
		// convenient for use with rich editors like tinyMCE or FCKEditor
		var veto = {};
		this.trigger('form-pre-serialize', [ this, options, veto ]);
		if (veto.veto) {
			log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
			return this;
		}

		// provide opportunity to alter form data before it is serialized
		if (options.beforeSerialize
				&& options.beforeSerialize(this, options) === false) {
			log('ajaxSubmit: submit aborted via beforeSerialize callback');
			return this;
		}

		var n, v, a = this.formToArray(options.semantic);
		if (options.data) {
			options.extraData = options.data;
			for (n in options.data) {
				if (options.data[n] instanceof Array) {
					for ( var k in options.data[n]) {
						a.push( {
							name : n,
							value : options.data[n][k]
						});
					}
				} else {
					v = options.data[n];
					v = $.isFunction(v) ? v() : v; // if value is fn, invoke it
					a.push( {
						name : n,
						value : v
					});
				}
			}
		}

		// give pre-submit callback an opportunity to abort the submit
		if (options.beforeSubmit
				&& options.beforeSubmit(a, this, options) === false) {
			log('ajaxSubmit: submit aborted via beforeSubmit callback');
			return this;
		}

		// fire vetoable 'validate' event
		this.trigger('form-submit-validate', [ a, this, options, veto ]);
		if (veto.veto) {
			log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
			return this;
		}

		var q = $.param(a);

		if (options.type.toUpperCase() == 'GET') {
			options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
			options.data = null; // data is null for 'get'
		} else {
			options.data = q; // data is the query string for 'post'
		}

		var $form = this, callbacks = [];
		if (options.resetForm) {
			callbacks.push(function() {
				$form.resetForm();
			});
		}
		if (options.clearForm) {
			callbacks.push(function() {
				$form.clearForm();
			});
		}

		// perform a load on the target only if dataType is not provided
		if (!options.dataType && options.target) {
			var oldSuccess = options.success || function() {
			};
			callbacks.push(function(data) {
				var fn = options.replaceTarget ? 'replaceWith' : 'html';
				$(options.target)[fn](data).each(oldSuccess, arguments);
			});
		} else if (options.success) {
			callbacks.push(options.success);
		}

		options.success = function(data, status, xhr) { // jQuery 1.4+ passes
														// xhr as 3rd arg
			var context = options.context || options; // jQuery 1.4+ supports
														// scope context
			for ( var i = 0, max = callbacks.length; i < max; i++) {
				callbacks[i].apply(context,
						[ data, status, xhr || $form, $form ]);
			}
		};

		// are there files to upload?
		var fileInputs = $('input:file', this).length > 0;
		var mp = 'multipart/form-data';
		var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);

		// options.iframe allows user to force iframe mode
		// 06-NOV-09: now defaulting to iframe mode if file input is detected
		if (options.iframe !== false
				&& (fileInputs || options.iframe || multipart)) {
			// hack to fix Safari hang (thanks to Tim Molendijk for this)
			// see:
			// http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
			if (options.closeKeepAlive) {
				$.get(options.closeKeepAlive, fileUpload);
			} else {
				fileUpload();
			}
		} else {
			$.ajax(options);
		}

		// fire 'notify' event
		this.trigger('form-submit-notify', [ this, options ]);
		return this;

		// private function for handling file uploads (hat tip to YAHOO!)
		function fileUpload() {
			var form = $form[0];

			if ($(':input[name=submit],:input[id=submit]', form).length) {
				// if there is an input with a name or id of 'submit' then we
				// won't be
				// able to invoke the submit fn on the form (at least not
				// x-browser)
				alert('Error: Form elements must not have name or id of "submit".');
				return;
			}

			var s = $.extend(true, {}, $.ajaxSettings, options);
			s.context = s.context || s;
			var id = 'jqFormIO' + (new Date().getTime()), fn = '_' + id;
			window[fn] = function() {
				var f = $io.data('form-plugin-onload');
				if (f) {
					f();
					window[fn] = undefined;
					try {
						delete window[fn];
					} catch (e) {
					}
				}
			}
			var $io = $('<iframe id="' + id + '" name="' + id + '" src="'
					+ s.iframeSrc + '" onload="window[\'_\'+this.id]()" />');
			var io = $io[0];

			$io.css( {
				position : 'absolute',
				top : '-1000px',
				left : '-1000px'
			});

			var xhr = { // mock object
				aborted : 0,
				responseText : null,
				responseXML : null,
				status : 0,
				statusText : 'n/a',
				getAllResponseHeaders : function() {
				},
				getResponseHeader : function() {
				},
				setRequestHeader : function() {
				},
				abort : function() {
					this.aborted = 1;
					$io.attr('src', s.iframeSrc); // abort op in progress
			}
			};

			var g = s.global;
			// trigger ajax global events so that activity/block indicators work
			// like normal
			if (g && !$.active++) {
				$.event.trigger("ajaxStart");
			}
			if (g) {
				$.event.trigger("ajaxSend", [ xhr, s ]);
			}

			if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
				if (s.global) {
					$.active--;
				}
				return;
			}
			if (xhr.aborted) {
				return;
			}

			var cbInvoked = false;
			var timedOut = 0;

			// add submitting element to data if we know it
			var sub = form.clk;
			if (sub) {
				var n = sub.name;
				if (n && !sub.disabled) {
					s.extraData = s.extraData || {};
					s.extraData[n] = sub.value;
					if (sub.type == "image") {
						s.extraData[n + '.x'] = form.clk_x;
						s.extraData[n + '.y'] = form.clk_y;
					}
				}
			}

			// take a breath so that pending repaints get some cpu time before
			// the upload starts
			function doSubmit() {
				// make sure form attrs are set
				var t = $form.attr('target'), a = $form.attr('action');

				// update form attrs in IE friendly way
				form.setAttribute('target', id);
				if (form.getAttribute('method') != 'POST') {
					form.setAttribute('method', 'POST');
				}
				if (form.getAttribute('action') != s.url) {
					form.setAttribute('action', s.url);
				}

				// ie borks in some cases when setting encoding
				if (!s.skipEncodingOverride) {
					$form.attr( {
						encoding : 'multipart/form-data',
						enctype : 'multipart/form-data'
					});
				}

				// support timout
				if (s.timeout) {
					setTimeout(function() {
						timedOut = true;
						cb();
					}, s.timeout);
				}

				// add "extra" data to form if provided in options
				var extraInputs = [];
				try {
					if (s.extraData) {
						for ( var n in s.extraData) {
							extraInputs.push($(
									'<input type="hidden" name="' + n
											+ '" value="' + s.extraData[n]
											+ '" />').appendTo(form)[0]);
						}
					}

					// add iframe to doc and submit the form
					$io.appendTo('body');
					$io.data('form-plugin-onload', cb);
					form.submit();
				} finally {
					// reset attrs and remove "extra" input elements
					form.setAttribute('action', a);
					if (t) {
						form.setAttribute('target', t);
					} else {
						$form.removeAttr('target');
					}
					$(extraInputs).remove();
				}
			}

			if (s.forceSync) {
				doSubmit();
			} else {
				setTimeout(doSubmit, 10); // this lets dom updates render
			}

			var data, doc, domCheckCount = 50;

			function cb() {
				if (cbInvoked) {
					return;
				}

				$io.removeData('form-plugin-onload');

				var ok = true;
				try {
					if (timedOut) {
						throw 'timeout';
					}
					// extract the server response from the iframe
					doc = io.contentWindow ? io.contentWindow.document
							: io.contentDocument ? io.contentDocument
									: io.document;

					var isXml = s.dataType == 'xml' || doc.XMLDocument
							|| $.isXMLDoc(doc);
					log('isXml=' + isXml);
					if (!isXml && window.opera
							&& (doc.body == null || doc.body.innerHTML == '')) {
						if (--domCheckCount) {
							// in some browsers (Opera) the iframe DOM is not
							// always traversable when
							// the onload callback fires, so we loop a bit to
							// accommodate
							log('requeing onLoad callback, DOM not available');
							setTimeout(cb, 250);
							return;
						}
						// let this fall through because server response could
						// be an empty document
						// log('Could not access iframe DOM after mutiple
						// tries.');
						// throw 'DOMException: not available';
					}

					// log('response detected');
					cbInvoked = true;
					xhr.responseText = doc.documentElement ? doc.documentElement.innerHTML
							: null;
					xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
					xhr.getResponseHeader = function(header) {
						var headers = {
							'content-type' : s.dataType
						};
						return headers[header];
					};

					var scr = /(json|script)/.test(s.dataType);
					if (scr || s.textarea) {
						// see if user embedded response in textarea
						var ta = doc.getElementsByTagName('textarea')[0];
						if (ta) {
							xhr.responseText = ta.value;
						} else if (scr) {
							// account for browsers injecting pre around json
							// response
							var pre = doc.getElementsByTagName('pre')[0];
							var b = doc.getElementsByTagName('body')[0];
							if (pre) {
								xhr.responseText = pre.innerHTML;
							} else if (b) {
								xhr.responseText = b.innerHTML;
							}
						}
					} else if (s.dataType == 'xml' && !xhr.responseXML
							&& xhr.responseText != null) {
						xhr.responseXML = toXml(xhr.responseText);
					}
					data = $.httpData(xhr, s.dataType);
				} catch (e) {
					log('error caught:', e);
					ok = false;
					xhr.error = e;
					$.handleError(s, xhr, 'error', e);
				}

				// ordering of these callbacks/triggers is odd, but that's how
				// $.ajax does it
				if (ok) {
					s.success.call(s.context, data, 'success', xhr);
					if (g) {
						$.event.trigger("ajaxSuccess", [ xhr, s ]);
					}
				}
				if (g) {
					$.event.trigger("ajaxComplete", [ xhr, s ]);
				}
				if (g && !--$.active) {
					$.event.trigger("ajaxStop");
				}
				if (s.complete) {
					s.complete.call(s.context, xhr, ok ? 'success' : 'error');
				}

				// clean up
				setTimeout(function() {
					$io.removeData('form-plugin-onload');
					$io.remove();
					xhr.responseXML = null;
				}, 100);
			}

			function toXml(s, doc) {
				if (window.ActiveXObject) {
					doc = new ActiveXObject('Microsoft.XMLDOM');
					doc.async = 'false';
					doc.loadXML(s);
				} else {
					doc = (new DOMParser()).parseFromString(s, 'text/xml');
				}
				return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc
						: null;
			}
		}
	};

	/**
	 * ajaxForm() provides a mechanism for fully automating form submission.
	 * 
	 * The advantages of using this method instead of ajaxSubmit() are:
	 * 
	 * 1: This method will include coordinates for <input type="image" />
	 * elements (if the element is used to submit the form). 2. This method will
	 * include the submit element's name/value data (for the element that was
	 * used to submit the form). 3. This method binds the submit() method to the
	 * form for you.
	 * 
	 * The options argument for ajaxForm works exactly as it does for
	 * ajaxSubmit. ajaxForm merely passes the options argument along after
	 * properly binding events for submit elements and the form itself.
	 */
	$.fn.ajaxForm = function(options) {
		// in jQuery 1.3+ we can fix mistakes with the ready state
		if (this.length === 0) {
			var o = {
				s : this.selector,
				c : this.context
			};
			if (!$.isReady && o.s) {
				log('DOM not ready, queuing ajaxForm');
				$(function() {
					$(o.s, o.c).ajaxForm(options);
				});
				return this;
			}
			// is your DOM ready?
			// http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
			log('terminating; zero elements found by selector' + ($.isReady ? ''
					: ' (DOM not ready)'));
			return this;
		}

		return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
			if (!e.isDefaultPrevented()) { // if event has been canceled, don't
											// proceed
					e.preventDefault();
					$(this).ajaxSubmit(options);
				}
			}).bind('click.form-plugin', function(e) {
			var target = e.target;
			var $el = $(target);
			if (!($el.is(":submit,input:image"))) {
				// is this a child element of the submit el? (ex: a span within
				// a button)
				var t = $el.closest(':submit');
				if (t.length == 0) {
					return;
				}
				target = t[0];
			}
			var form = this;
			form.clk = target;
			if (target.type == 'image') {
				if (e.offsetX != undefined) {
					form.clk_x = e.offsetX;
					form.clk_y = e.offsetY;
				} else if (typeof $.fn.offset == 'function') { // try to use
																// dimensions
																// plugin
					var offset = $el.offset();
					form.clk_x = e.pageX - offset.left;
					form.clk_y = e.pageY - offset.top;
				} else {
					form.clk_x = e.pageX - target.offsetLeft;
					form.clk_y = e.pageY - target.offsetTop;
				}
			}
			// clear form vars
			setTimeout(function() {
				form.clk = form.clk_x = form.clk_y = null;
			}, 100);
		});
	};

	// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
	$.fn.ajaxFormUnbind = function() {
		return this.unbind('submit.form-plugin click.form-plugin');
	};

	/**
	 * formToArray() gathers form element data into an array of objects that can
	 * be passed to any of the following ajax functions: $.get, $.post, or load.
	 * Each object in the array has both a 'name' and 'value' property. An
	 * example of an array for a simple login form might be:
	 *  [ { name: 'username', value: 'jresig' }, { name: 'password', value:
	 * 'secret' } ]
	 * 
	 * It is this array that is passed to pre-submit callback functions provided
	 * to the ajaxSubmit() and ajaxForm() methods.
	 */
	$.fn.formToArray = function(semantic) {
		var a = [];
		if (this.length === 0) {
			return a;
		}

		var form = this[0];
		var els = semantic ? form.getElementsByTagName('*') : form.elements;
		if (!els) {
			return a;
		}

		var i, j, n, v, el, max, jmax;
		for (i = 0, max = els.length; i < max; i++) {
			el = els[i];
			n = el.name;
			if (!n) {
				continue;
			}

			if (semantic && form.clk && el.type == "image") {
				// handle image inputs on the fly when semantic == true
				if (!el.disabled && form.clk == el) {
					a.push( {
						name : n,
						value : $(el).val()
					});
					a.push( {
						name : n + '.x',
						value : form.clk_x
					}, {
						name : n + '.y',
						value : form.clk_y
					});
				}
				continue;
			}

			v = $.fieldValue(el, true);
			if (v && v.constructor == Array) {
				for (j = 0, jmax = v.length; j < jmax; j++) {
					a.push( {
						name : n,
						value : v[j]
					});
				}
			} else if (v !== null && typeof v != 'undefined') {
				a.push( {
					name : n,
					value : v
				});
			}
		}

		if (!semantic && form.clk) {
			// input type=='image' are not found in elements array! handle it
			// here
			var $input = $(form.clk), input = $input[0];
			n = input.name;
			if (n && !input.disabled && input.type == 'image') {
				a.push( {
					name : n,
					value : $input.val()
				});
				a.push( {
					name : n + '.x',
					value : form.clk_x
				}, {
					name : n + '.y',
					value : form.clk_y
				});
			}
		}
		return a;
	};

	/**
	 * Serializes form data into a 'submittable' string. This method will return
	 * a string in the format: name1=value1&amp;name2=value2
	 */
	$.fn.formSerialize = function(semantic) {
		// hand off to jQuery.param for proper encoding
		return $.param(this.formToArray(semantic));
	};

	/**
	 * Serializes all field elements in the jQuery object into a query string.
	 * This method will return a string in the format:
	 * name1=value1&amp;name2=value2
	 */
	$.fn.fieldSerialize = function(successful) {
		var a = [];
		this.each(function() {
			var n = this.name;
			if (!n) {
				return;
			}
			var v = $.fieldValue(this, successful);
			if (v && v.constructor == Array) {
				for ( var i = 0, max = v.length; i < max; i++) {
					a.push( {
						name : n,
						value : v[i]
					});
				}
			} else if (v !== null && typeof v != 'undefined') {
				a.push( {
					name : this.name,
					value : v
				});
			}
		});
		// hand off to jQuery.param for proper encoding
		return $.param(a);
	};

	/**
	 * Returns the value(s) of the element in the matched set. For example,
	 * consider the following form:
	 * 
	 * <form><fieldset> <input name="A" type="text" /> <input name="A"
	 * type="text" /> <input name="B" type="checkbox" value="B1" /> <input
	 * name="B" type="checkbox" value="B2"/> <input name="C" type="radio"
	 * value="C1" /> <input name="C" type="radio" value="C2" /> </fieldset></form>
	 * 
	 * var v = $(':text').fieldValue(); // if no values are entered into the
	 * text inputs v == ['',''] // if values entered into the text inputs are
	 * 'foo' and 'bar' v == ['foo','bar']
	 * 
	 * var v = $(':checkbox').fieldValue(); // if neither checkbox is checked v
	 * === undefined // if both checkboxes are checked v == ['B1', 'B2']
	 * 
	 * var v = $(':radio').fieldValue(); // if neither radio is checked v ===
	 * undefined // if first radio is checked v == ['C1']
	 * 
	 * The successful argument controls whether or not the field element must be
	 * 'successful' (per
	 * http://www.w3.org/TR/html4/interact/forms.html#successful-controls). The
	 * default value of the successful argument is true. If this value is false
	 * the value(s) for each element is returned.
	 * 
	 * Note: This method *always* returns an array. If no valid value can be
	 * determined the array will be empty, otherwise it will contain one or more
	 * values.
	 */
	$.fn.fieldValue = function(successful) {
		for ( var val = [], i = 0, max = this.length; i < max; i++) {
			var el = this[i];
			var v = $.fieldValue(el, successful);
			if (v === null || typeof v == 'undefined'
					|| (v.constructor == Array && !v.length)) {
				continue;
			}
			v.constructor == Array ? $.merge(val, v) : val.push(v);
		}
		return val;
	};

	/**
	 * Returns the value of the field element.
	 */
	$.fieldValue = function(el, successful) {
		var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
		if (successful === undefined) {
			successful = true;
		}

		if (successful
				&& (!n || el.disabled || t == 'reset' || t == 'button'
						|| (t == 'checkbox' || t == 'radio') && !el.checked
						|| (t == 'submit' || t == 'image') && el.form
						&& el.form.clk != el || tag == 'select'
						&& el.selectedIndex == -1)) {
			return null;
		}

		if (tag == 'select') {
			var index = el.selectedIndex;
			if (index < 0) {
				return null;
			}
			var a = [], ops = el.options;
			var one = (t == 'select-one');
			var max = (one ? index + 1 : ops.length);
			for ( var i = (one ? index : 0); i < max; i++) {
				var op = ops[i];
				if (op.selected) {
					var v = op.value;
					if (!v) { // extra pain for IE...
						v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text
								: op.value;
					}
					if (one) {
						return v;
					}
					a.push(v);
				}
			}
			return a;
		}
		return $(el).val();
	};

	/**
	 * Clears the form data. Takes the following actions on the form's input
	 * fields: - input text fields will have their 'value' property set to the
	 * empty string - select elements will have their 'selectedIndex' property
	 * set to -1 - checkbox and radio inputs will have their 'checked' property
	 * set to false - inputs of type submit, button, reset, and hidden will
	 * *not* be effected - button elements will *not* be effected
	 */
	$.fn.clearForm = function() {
		return this.each(function() {
			$('input,select,textarea', this).clearFields();
		});
	};

	/**
	 * Clears the selected form elements.
	 */
	$.fn.clearFields = $.fn.clearInputs = function() {
		return this.each(function() {
			var t = this.type, tag = this.tagName.toLowerCase();
			if (t == 'text' || t == 'password' || tag == 'textarea') {
				this.value = '';
			} else if (t == 'checkbox' || t == 'radio') {
				this.checked = false;
			} else if (tag == 'select') {
				this.selectedIndex = -1;
			}
		});
	};

	/**
	 * Resets the form data. Causes all form elements to be reset to their
	 * original value.
	 */
	$.fn.resetForm = function() {
		return this.each(function() {
			// guard against an input with the name of 'reset'
				// note that IE reports the reset function as an 'object'
				if (typeof this.reset == 'function'
						|| (typeof this.reset == 'object' && !this.reset.nodeType)) {
					this.reset();
				}
			});
	};

	/**
	 * Enables or disables any matching elements.
	 */
	$.fn.enable = function(b) {
		if (b === undefined) {
			b = true;
		}
		return this.each(function() {
			this.disabled = !b;
		});
	};

	/**
	 * Checks/unchecks any matching checkboxes or radio buttons and
	 * selects/deselects and matching option elements.
	 */
	$.fn.selected = function(select) {
		if (select === undefined) {
			select = true;
		}
		return this.each(function() {
			var t = this.type;
			if (t == 'checkbox' || t == 'radio') {
				this.checked = select;
			} else if (this.tagName.toLowerCase() == 'option') {
				var $sel = $(this).parent('select');
				if (select && $sel[0] && $sel[0].type == 'select-one') {
					// deselect all other options
				$sel.find('option').selected(false);
			}
			this.selected = select;
		}
	})	;
	};

	// helper fn for console logging
	// set $.fn.ajaxSubmit.debug to true to enable debug logging
	function log() {
		if ($.fn.ajaxSubmit.debug) {
			var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,
					'');
			if (window.console && window.console.log) {
				window.console.log(msg);
			} else if (window.opera && window.opera.postError) {
				window.opera.postError(msg);
			}
		}
	}
	;

})(jQuery);
